#ifndef TB_ARCHIVE_MESSAGE_PACK_WRITER_H__
#define TB_ARCHIVE_MESSAGE_PACK_WRITER_H__

/*
//////////////////////////////////////////////////////////////////////////
  format name	      first byte (in binary)	first byte (in hex)
  positive fixint	  0xxxxxxx	              0x00 - 0x7f
  fixmap	          1000xxxx	              0x80 - 0x8f
  fixarray	        1001xxxx	              0x90 - 0x9f
  fixstr	          101xxxxx	              0xa0 - 0xbf
  nil	              11000000	              0xc0
  (never used)	    11000001	              0xc1
  false	            11000010	              0xc2
  true	            11000011	              0xc3
  bin 8	            11000100	              0xc4
  bin 16	          11000101	              0xc5
  bin 32	          11000110	              0xc6
  ext 8	            11000111	              0xc7
  ext 16	          11001000	              0xc8
  ext 32	          11001001	              0xc9
  float 32	        11001010	              0xca
  float 64	        11001011	              0xcb
  uint 8	          11001100	              0xcc
  uint 16	          11001101	              0xcd
  uint 32	          11001110	              0xce
  uint 64	          11001111	              0xcf
  int 8	            11010000	              0xd0
  int 16	          11010001	              0xd1
  int 32	          11010010	              0xd2
  int 64	          11010011	              0xd3
  fixext 1	        11010100	              0xd4
  fixext 2	        11010101	              0xd5
  fixext 4	        11010110	              0xd6
  fixext 8	        11010111	              0xd7
  fixext 16	        11011000	              0xd8
  str 8	            11011001	              0xd9
  str 16	          11011010	              0xda
  str 32	          11011011	              0xdb
  array 16	        11011100	              0xdc
  array 32	        11011101	              0xdd
  map 16	          11011110	              0xde
  map 32	          11011111	              0xdf
  negative fixint	  111xxxxx	              0xe0 - 0xff
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
  Notation in diagrams

  one byte:
  +--------+
  |        |
  +--------+

  a variable number of bytes:
  +========+
  |        |
  +========+

  variable number of objects stored in MessagePack format:
  +~~~~~~~~~~~~~~~~~+
  |                 |
  +~~~~~~~~~~~~~~~~~+
  X, Y, Z and A are the symbols that will be replaced by an actual bit.

  nil format

  Nil format stores nil in 1 byte.

  nil:
  +--------+
  |  0xc0  |
  +--------+

  bool format family

  Bool format family stores false or true in 1 byte.

  false:
  +--------+
  |  0xc2  |
  +--------+

  true:
  +--------+
  |  0xc3  |
  +--------+

  int format family

  Int format family stores an integer in 1, 2, 3, 5, or 9 bytes.

  positive fixnum stores 7-bit positive integer
  +--------+
  |0XXXXXXX|
  +--------+

  negative fixnum stores 5-bit negative integer
  +--------+
  |111YYYYY|
  +--------+

  * 0XXXXXXX is 8-bit unsigned integer
  * 111YYYYY is 8-bit signed integer

  uint 8 stores a 8-bit unsigned integer
  +--------+--------+
  |  0xcc  |ZZZZZZZZ|
  +--------+--------+

  uint 16 stores a 16-bit big-endian unsigned integer
  +--------+--------+--------+
  |  0xcd  |ZZZZZZZZ|ZZZZZZZZ|
  +--------+--------+--------+

  uint 32 stores a 32-bit big-endian unsigned integer
  +--------+--------+--------+--------+--------+
  |  0xce  |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|
  +--------+--------+--------+--------+--------+

  uint 64 stores a 64-bit big-endian unsigned integer
  +--------+--------+--------+--------+--------+--------+--------+--------+--------+
  |  0xcf  |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|
  +--------+--------+--------+--------+--------+--------+--------+--------+--------+

  int 8 stores a 8-bit signed integer
  +--------+--------+
  |  0xd0  |ZZZZZZZZ|
  +--------+--------+

  int 16 stores a 16-bit big-endian signed integer
  +--------+--------+--------+
  |  0xd1  |ZZZZZZZZ|ZZZZZZZZ|
  +--------+--------+--------+

  int 32 stores a 32-bit big-endian signed integer
  +--------+--------+--------+--------+--------+
  |  0xd2  |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|
  +--------+--------+--------+--------+--------+

  int 64 stores a 64-bit big-endian signed integer
  +--------+--------+--------+--------+--------+--------+--------+--------+--------+
  |  0xd3  |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|
  +--------+--------+--------+--------+--------+--------+--------+--------+--------+

  float format family

  Float format family stores a floating point number in 5 bytes or 9 bytes.

  float 32 stores a floating point number in IEEE 754 single precision floating point number format:
  +--------+--------+--------+--------+--------+
  |  0xca  |XXXXXXXX|XXXXXXXX|XXXXXXXX|XXXXXXXX|
  +--------+--------+--------+--------+--------+

  float 64 stores a floating point number in IEEE 754 double precision floating point number format:
  +--------+--------+--------+--------+--------+--------+--------+--------+--------+
  |  0xcb  |YYYYYYYY|YYYYYYYY|YYYYYYYY|YYYYYYYY|YYYYYYYY|YYYYYYYY|YYYYYYYY|YYYYYYYY|
  +--------+--------+--------+--------+--------+--------+--------+--------+--------+

  where
  * XXXXXXXX_XXXXXXXX_XXXXXXXX_XXXXXXXX is a big-endian IEEE 754 single precision floating point number
  * YYYYYYYY_YYYYYYYY_YYYYYYYY_YYYYYYYY_YYYYYYYY_YYYYYYYY_YYYYYYYY_YYYYYYYY is a big-endian
  IEEE 754 double precision floating point number

  str format family

  Str format family stores a byte array in 1, 2, 3, or 5 bytes of extra bytes in addition to the size of the byte array.

  fixstr stores a byte array whose length is upto 31 bytes:
  +--------+========+
  |101XXXXX|  data  |
  +--------+========+

  str 8 stores a byte array whose length is upto (2^8)-1 bytes:
  +--------+--------+========+
  |  0xd9  |YYYYYYYY|  data  |
  +--------+--------+========+

  str 16 stores a byte array whose length is upto (2^16)-1 bytes:
  +--------+--------+--------+========+
  |  0xda  |ZZZZZZZZ|ZZZZZZZZ|  data  |
  +--------+--------+--------+========+

  str 32 stores a byte array whose length is upto (2^32)-1 bytes:
  +--------+--------+--------+--------+--------+========+
  |  0xdb  |AAAAAAAA|AAAAAAAA|AAAAAAAA|AAAAAAAA|  data  |
  +--------+--------+--------+--------+--------+========+

  where
  * XXXXX is a 5-bit unsigned integer which represents N
  * YYYYYYYY is a 8-bit unsigned integer which represents N
  * ZZZZZZZZ_ZZZZZZZZ is a 16-bit big-endian unsigned integer which represents N
  * AAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA is a 32-bit big-endian unsigned integer which represents N
  * N is the length of data

  bin format family

  Bin format family stores an byte array in 2, 3, or 5 bytes of extra bytes in addition to the size of the byte array.

  bin 8 stores a byte array whose length is upto (2^8)-1 bytes:
  +--------+--------+========+
  |  0xc4  |XXXXXXXX|  data  |
  +--------+--------+========+

  bin 16 stores a byte array whose length is upto (2^16)-1 bytes:
  +--------+--------+--------+========+
  |  0xc5  |YYYYYYYY|YYYYYYYY|  data  |
  +--------+--------+--------+========+

  bin 32 stores a byte array whose length is upto (2^32)-1 bytes:
  +--------+--------+--------+--------+--------+========+
  |  0xc6  |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|  data  |
  +--------+--------+--------+--------+--------+========+

  where
  * XXXXXXXX is a 8-bit unsigned integer which represents N
  * YYYYYYYY_YYYYYYYY is a 16-bit big-endian unsigned integer which represents N
  * ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ is a 32-bit big-endian unsigned integer which represents N
  * N is the length of data

  array format family

  Array format family stores a sequence of elements in 1, 3, or 5 bytes of extra bytes in addition to the elements.

  fixarray stores an array whose length is upto 15 elements:
  +--------+~~~~~~~~~~~~~~~~~+
  |1001XXXX|    N objects    |
  +--------+~~~~~~~~~~~~~~~~~+

  array 16 stores an array whose length is upto (2^16)-1 elements:
  +--------+--------+--------+~~~~~~~~~~~~~~~~~+
  |  0xdc  |YYYYYYYY|YYYYYYYY|    N objects    |
  +--------+--------+--------+~~~~~~~~~~~~~~~~~+

  array 32 stores an array whose length is upto (2^32)-1 elements:
  +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+
  |  0xdd  |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|    N objects    |
  +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+

  where
  * XXXX is a 4-bit unsigned integer which represents N
  * YYYYYYYY_YYYYYYYY is a 16-bit big-endian unsigned integer which represents N
  * ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ is a 32-bit big-endian unsigned integer which represents N
  N is the size of a array

  map format family

  Map format family stores a sequence of key-value pairs in 1, 3, or 5 bytes of extra bytes in addition to the key-value pairs.

  fixmap stores a map whose length is upto 15 elements
  +--------+~~~~~~~~~~~~~~~~~+
  |1000XXXX|   N*2 objects   |
  +--------+~~~~~~~~~~~~~~~~~+

  map 16 stores a map whose length is upto (2^16)-1 elements
  +--------+--------+--------+~~~~~~~~~~~~~~~~~+
  |  0xde  |YYYYYYYY|YYYYYYYY|   N*2 objects   |
  +--------+--------+--------+~~~~~~~~~~~~~~~~~+

  map 32 stores a map whose length is upto (2^32)-1 elements
  +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+
  |  0xdf  |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|   N*2 objects   |
  +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+

  where
  * XXXX is a 4-bit unsigned integer which represents N
  * YYYYYYYY_YYYYYYYY is a 16-bit big-endian unsigned integer which represents N
  * ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ is a 32-bit big-endian unsigned integer which represents N
  * N is the size of a map
  * odd elements in objects are keys of a map
  * the next element of a key is its associated value

  ext format family

  Ext format family stores a tuple of an integer and a byte array.

  fixext 1 stores an integer and a byte array whose length is 1 byte
  +--------+--------+--------+
  |  0xd4  |  type  |  data  |
  +--------+--------+--------+

  fixext 2 stores an integer and a byte array whose length is 2 bytes
  +--------+--------+--------+--------+
  |  0xd5  |  type  |       data      |
  +--------+--------+--------+--------+

  fixext 4 stores an integer and a byte array whose length is 4 bytes
  +--------+--------+--------+--------+--------+--------+
  |  0xd6  |  type  |                data               |
  +--------+--------+--------+--------+--------+--------+

  fixext 8 stores an integer and a byte array whose length is 8 bytes
  +--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
  |  0xd7  |  type  |                                  data                                 |
  +--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+

  fixext 16 stores an integer and a byte array whose length is 16 bytes
  +--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
  |  0xd8  |  type  |                                  data                                  
  +--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
  +--------+--------+--------+--------+--------+--------+--------+--------+
  data (cont.)                              |
  +--------+--------+--------+--------+--------+--------+--------+--------+

  ext 8 stores an integer and a byte array whose length is upto (2^8)-1 bytes:
  +--------+--------+--------+========+
  |  0xc7  |XXXXXXXX|  type  |  data  |
  +--------+--------+--------+========+

  ext 16 stores an integer and a byte array whose length is upto (2^16)-1 bytes:
  +--------+--------+--------+--------+========+
  |  0xc8  |YYYYYYYY|YYYYYYYY|  type  |  data  |
  +--------+--------+--------+--------+========+

  ext 32 stores an integer and a byte array whose length is upto (2^32)-1 bytes:
  +--------+--------+--------+--------+--------+--------+========+
  |  0xc9  |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|  type  |  data  |
  +--------+--------+--------+--------+--------+--------+========+
  where
  * XXXXXXXX is a 8-bit unsigned integer which represents N
  * YYYYYYYY_YYYYYYYY is a 16-bit big-endian unsigned integer which represents N
  * ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ is a big-endian 32-bit unsigned integer which represents N
  * N is a length of data
  * type is a signed 8-bit signed integer
  * type < 0 is reserved for future extension including 2-byte type information
//////////////////////////////////////////////////////////////////////////
*/

TB_NAMESPACE_BEGIN

namespace detail {
  template <typename OutputBuffer>
  struct MsgPackWriteHelper {
    template <typename T>
    static void WriteValue(OutputBuffer& os, const T& val) {
      boost::ignore_unused(os, val);
    }

		template <>
		static void WriteValue(OutputBuffer& os, const bool& val) {
			os.Write<uint8>(val ? 0xc3 : 0xc2);
		}

    template <>
    static void WriteValue(OutputBuffer& os, const int8& val) {
      os.Write<uint8>(0xd0);
      os.Write(val);
    }

    template <>
    static void WriteValue(OutputBuffer& os, const uint8& val) {
      os.Write<uint8>(0xcc);
      os.Write(val);
    }

    template <>
    static void WriteValue(OutputBuffer& os, const int16& val) {
      os.Write<uint8>(0xd1);
      os.Write(val);
    }

    template <>
    static void WriteValue(OutputBuffer& os, const uint16& val) {
      os.Write<uint8>(0xcd);
      os.Write(val);
    }

    template <>
    static void WriteValue(OutputBuffer& os, const int32& val) {
      os.Write<uint8>(0xd2);
      os.Write(val);
    }

    template <>
    static void WriteValue(OutputBuffer& os, const uint32& val) {
      os.Write<uint8>(0xce);
      os.Write(val);
    }

    template <>
    static void WriteValue(OutputBuffer& os, const int64& val) {
      os.Write<uint8>(0xd3);
      os.Write(val);
    }

    template <>
    static void WriteValue(OutputBuffer& os, const uint64& val) {
      os.Write<uint8>(0xcf);
      os.Write(val);
    }

    template <>
    static void WriteValue(OutputBuffer& os, const float& val) {
      os.Write<uint8>(0xca);
      os.Write(val);
    }

    template <>
    static void WriteValue(OutputBuffer& os, const double& val) {
      os.Write<uint8>(0xcb);
      os.Write(val);
    }

    template <>
    static void WriteValue(OutputBuffer& os, const Duration& val) {
      WriteValue(os, val.TotalMilliseconds());
    }

    template <>
    static void WriteValue(OutputBuffer& os, const DateTime& val) {
      WriteValue(os, val.TotalMilliseconds());
    }

		template <>
		static void WriteValue(OutputBuffer& os, const UniqueId& val) {
			WriteValue(os, val.ToString());
		}

    template <>
    static void WriteValue(OutputBuffer& os, const std::string& val) {
      if (val.size() <= 0xFF) {
        os.Write<uint8>(0xd9);
        os.Write<uint8>((uint8)val.size());
      } else if (val.size() <= 0xFFFF) {
        os.Write<uint8>(0xda);
        os.Write<uint16>((uint16)val.size());
      } else if (val.size() <= 0xFFFFFFFF) {
        os.Write<uint8>(0xdb);
        os.Write<uint32>((uint32)val.size());
      } else {
        TB_ASSERT(false && "too long string");
      }
      os.WriteBytes(val.data(), (uint32)val.size());
    }

    template <>
    static void WriteValue(OutputBuffer& os, const string16& val) {
      std::string u8str = UTF16ToUTF8(val);
      WriteValue(os, u8str);
    }
  };
}

template <typename OutputBuffer>
class MsgPackWriter {
  typedef detail::MsgPackWriteHelper<OutputBuffer> write_helper;

 public:
  MsgPackWriter(OutputBuffer& os) : os_(os) {}

  void WriteStructBegin(const reflection::TypeInfo* info, uint32 size) {
    //fix to 4 words
    os_.Write<uint8>(0xc9);

    writeStack_.push(os_.WriteIndex());
    os_.Write<uint32>(0);
    
    write_helper::WriteValue(os_, info->name);
    os_.Write<uint32>(size);
  }

  void WriteStructEnd() {
    TB_ASSERT(writeStack_.size() > 0);

    uint32 currWriteIndex = os_.WriteIndex();
    uint32 preSizeWriteIndex = writeStack_.top();
    writeStack_.pop();
    os_.SeekWrite(preSizeWriteIndex);
    os_.Write<uint32>(currWriteIndex - preSizeWriteIndex);
    os_.SeekWrite(currWriteIndex);
  }

  void WriteFieldBegin(const reflection::Property* info) {
    write_helper::WriteValue(os_, info->propName);
  }

  void WriteFieldEnd() {}

  void WriteArrayBegin(uint32 size) {
    if (size <= 0xF) {
      os_.Write<uint8>(0x90 + size);
    } else if (size <= 0xFFFF) {
      os_.Write<uint8>(0xdc);
      os_.Write<uint16>(size);
    } else if (size < 0xFFFFFFFF) {
      os_.Write(0xdd);
      os_.Write<uint32>(size);
    } else {
      TB_ASSERT(false && "invalid container size");
    }
  }

  void WriteArrayEnd() {}

  void WriteMapBegin(uint32 size) {
    if (size <= 0xF) {
      os_.Write<uint8>(0x80 + size);
    } else if (size <= 0xFFFF) {
      os_.Write<uint8>(0xde);
      os_.Write<uint16>(size);
    } else if (size < 0xFFFFFFFF) {
      os_.Write<uint8>(0xdf);
      os_.Write<uint32>(size);
    } else {
      TB_ASSERT(false && "invalid container size");
    }
  }

  void WriteMapEnd() {}

	void WritePairBegin() {
		WriteMapBegin(1);
	}

	void WritePairEnd() {}

  template <typename T> 
  bool WriteValue(const T& val) {
    write_helper::WriteValue(os_, val);
    return true;
  } 

  void Flush() {}

 private:
  OutputBuffer& os_;
  std::stack<uint32> writeStack_;
};

TB_NAMESPACE_END

#endif // TB_ARCHIVE_MESSAGE_PACK_WRITER_H__
